import UIKit

/* 定义和调用 */

// 1
func printCaptitalInfo (name : String, country : String,  population : Int) {
    print("\(name) is the capital of \(country) and its population is \(population) million")
}

printCaptitalInfo(name: "Beijing", country: "China", population:  23)

// 2
func calculateCapitalInfo (name : String, country : String,  population : Int) -> (String, Int){
    let returnStr : String = "\(name) is the capital of \(country) and its population is \(population) million";
    return (returnStr, returnStr.count)
}

print(calculateCapitalInfo(name: "Beijing", country: "China", population:  23))

/* 函数形参 */

// 1
func computeMultiply() -> Int {
    var result : Int = 1
    for i in 1...10 {
        result = result * i
    }
    return result
}

print(computeMultiply())

// 2
func printMultiply() {
    var result : Int = 1
    for i in 1...10 {
        result = result * i
    }
    print(result)
}

printMultiply()

// 3

func multipliers(numbers: Int...) -> Int {
    var result : Int = 1
    for i in numbers {
        result = result * i
    }
    return result
}

print(multipliers(numbers: 1, 2, 3))

// 4
var numbers : [Int] = [3, 2, 1]
print(numbers)

func sortArray(intArray: inout [Int]) {
    intArray = intArray.sorted();
}

sortArray(intArray: &numbers)
print(numbers)
// inout类型会将函数中对于形参的修改传导到实参

/* 函数类型 */
// 1
var printAction : () -> () = printMultiply
var mathOperation : (Int...) -> Int = multipliers

printAction()
mathOperation(2, 3, 4)

// 2
func multiply (a: Int, b: Int) -> Int {
    return a * b
}

func divide (a: Int, b: Int) -> Int {
    return a / b
}

func printMathOperation (op: (Int, Int) -> Int, a : Int, b :  Int) -> () {
    if a >= b {
        print(op(a, b))
    } else {
        print(op(b, a))
    }
}

printMathOperation(op: multiply, a: 2, b: 4)
printMathOperation(op: divide, a: 2, b: 4)

// 3
func multiplyOrDivideOperation(op: String) -> ((Int, Int)->Int) {
    if (op == "Multiply") {
        return multiply
    } else {
        return divide
    }
}

var operation : (Int, Int)->Int = multiplyOrDivideOperation(op: "Multiply")
printMathOperation(op: operation, a: 2, b: 4)

/* 嵌套函数 */

func multiplyOrDivideOperationChange(op: String, num1: Int, num2: Int) -> Int {
    func multiplyChange (a: Int, b: Int) -> Int {
        return a * b
    }

    func divideChange (a: Int, b: Int) -> Int {
        return a / b
    }
    
    if (op == "Multiply") {
        return multiplyChange(a: num1, b: num2)
    } else {
        return divideChange(a: num1, b: num2)
    }
}
print(multiplyOrDivideOperationChange(op: "Multiply", num1: 3, num2: 4))

/* 闭包表达式 */

// 1
var compareClosure = {(num1: Int, num2: Int) -> Bool in
    return num1 < num2
}

compareClosure(8, 10)
compareClosure(10, 8)

// 2
let numbers2 : [Int] = [88,128,9,3,66,16,8]

numbers2.sorted(by: {(num1: Int, num2: Int) -> Bool in
    return num1 < num2
})

numbers2.sorted(by: <)

numbers2.sorted(by: compareClosure)

// 没有差别，本质上都是传递了作用相同的闭包

// 3
var calculateClosure = {() -> Void in
    var result = 0
    for i in 1...100 {
        result = result + i
    }
    print(result)
}

// 4
numbers2.sorted(){(num1: Int, num2: Int) -> Bool in
    return num1 < num2
}

/* 闭包的应用 */

// 1
func marathonRace(speed: Int, racer: String) -> (() -> Int) {
    var remainDistance = 421950
    // 闭包可以在其定义的上下文中捕获常量或变量。
    // 即使定义这些常量和变量的原域已经不存在，闭包仍然可以在闭包函数体内引用和修改这些值。
    // 在闭包内访问了remainDistance变量，即捕获在包含它的函数体内已经存在的remainDistance变量。
    // 并存储了该变量的一个副本，而该副本随着racing一同被存储。
    var racing = {
        remainDistance = remainDistance - speed
        return remainDistance
    }
    return racing
}

// 闭包是引用类型
let runnerJack = marathonRace(speed: 280, racer: "Jack")
let runnerTom = marathonRace(speed: 315, racer: "Tom")

var nowJack: Int
var nowTom : Int

// 定义一个变量i没有用处，如果为了循环一定的次数可以直接用_
for _ in 1...1600 {
    nowJack = runnerJack()
    nowTom = runnerTom()
    
    if nowJack <= 0 {
        print("Jack Win!")
        break
    }
    
    if nowTom <= 0 {
        print("Tom Win!")
        break
    }
    
    print("\(nowJack) \(nowTom)")
}

// 2

var captitals : [String] = ["Beijing", "Washington", "London", "Paris", "Tokyo"]

// 函数只需要闭包表达式一个参数，使用尾随闭包时可以把()省略
// forEach方法遍历集合中的每一个元素，传给闭包的第一个参数，通过闭包对元素进行操作
captitals.forEach{print($0)}
// filter方法按照一定条件过滤集合中的元素，形成一个新的集合，过滤的条件由闭包提供，返回true的保留
let filterCaptitals = captitals.filter{return $0.contains("o")}
// map方法遍历集合中的每一个元素，传给闭包的第一个参数，闭包中对元素进行一定的操作，形成一个新的集合
let mapCaptitals = captitals.map{return $0.lowercased()}
// reduce方法：Returns the result of combining the elements of the sequence using the given closure.
// 第一个参数为起始值；
// 第二个参数为闭包。第一个参数为当前值，另一个为当前遍历到的集合的元素
let stringCaptitals = captitals.reduce("Capitals are: "){return $0 + $1}

print(filterCaptitals)
print(mapCaptitals)
print(stringCaptitals)
